package com.tuling.nettyWebSocketClient;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.concurrent.DefaultThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;

/**
 * https://blog.csdn.net/iaoyou1/article/details/111239512
 */
public class WebsocketClient extends AbstractWebsocketClient {

    private static final Logger log = LoggerFactory.getLogger(WebsocketClient.class);

    private static final NioEventLoopGroup NIO_GROUP = new NioEventLoopGroup(1, new DefaultThreadFactory("client"));

    private final URI uri;

    private final int port;

    private Bootstrap bootstrap;

    private WebsocketClientHandler handler;

    private Channel channel;

    public WebsocketClient(String url, int connectionTimeOut) throws URISyntaxException {
        super(connectionTimeOut);
        this.uri = new URI(url);
        this.port = getPort();
    }

    private int getPort() {
        int port = uri.getPort();
        if (port == -1) {
            String scheme = uri.getScheme();
            if ("wss".equals(scheme)) {
                return 443;
            } else if ("ws".equals(scheme)) {
                return 80;
            } else {
                throw new RuntimeException("unknown scheme: " + scheme);
            }
        }
        return port;
    }

    @Override
    protected void doOpen() {
        // websocket 客户端握手实现的基类
        WebSocketClientHandshaker webSocketClientHandshaker = WebSocketClientHandshakerFactory.newHandshaker(uri, WebSocketVersion.V13, null, true, new DefaultHttpHeaders());
        // 业务处理类
        handler = new WebsocketClientHandler(webSocketClientHandshaker, this.websocketContext);
        // client
        bootstrap = new Bootstrap();
        // 添加管道
        bootstrap.group(NIO_GROUP).channel(NioSocketChannel.class)
                .handler(new WebsocketChannelInitializer(handler));
    }

    @Override
    protected void doConnect() throws Exception {
        try {
            // 启动连接
            ChannelFuture channelFuture = bootstrap.connect(uri.getHost(), port).sync();
            this.channel = channelFuture.channel();
            // 等待握手
            handler.handshakeFuture().sync();
        } catch (InterruptedException e) {
            log.error(e.getMessage(), e);
            Thread.currentThread().interrupt();
        }
    }

    @Override
    protected Channel getChannel() {
        return this.channel;
    }

    @Override
    public void close() {
        if (channel != null) {
            channel.close();
        }
    }

    public boolean isOpen() {
        return channel.isOpen();
    }

    private class WebsocketChannelInitializer extends ChannelInitializer<SocketChannel> {
        private WebsocketClientHandler handler;

        public WebsocketChannelInitializer(WebsocketClientHandler handler) {
            this.handler = handler;
        }

        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            ChannelPipeline pipeline = ch.pipeline();
            pipeline.addLast("logging", new LoggingHandler("DEBUG"));//设置log监听器，并且日志级别为debug，方便观察运行流程
            pipeline.addLast("http-client-Codec", new HttpClientCodec());
            pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
//            // 对应服务端来说 WebSocketServerCompressionHandler
//            pipeline.addLast("web-socket-client-compression-handler", WebSocketClientCompressionHandler.INSTANCE);
            pipeline.addLast("web-socket-client-handler", handler);
        }
    }

    public static void main(String[] args) {
        try (WebsocketClient websocketClient = new WebsocketClient("ws://127.0.0.1:8081/ws", 15)) {
            websocketClient.connect();
            TimeUnit.SECONDS.sleep(2);
            while (true) {
                Scanner scan = new Scanner(System.in);
                System.out.println("请输入");
                String s = scan.nextLine();
                websocketClient.write(s);
                String result = websocketClient.receiveResult();
                log.info("结果：{}", result);
            }
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
